import * as THREE from "three";

import shaderGradient from './shaderGradient'

// Fix Error: Can not resolve #include <uv2_pars_vertex>, <... (it is needed from three.js version 0.151.3)
THREE.ShaderChunk['uv2_pars_vertex'] = ``
THREE.ShaderChunk['uv2_vertex'] = ``
THREE.ShaderChunk['uv2_pars_fragment'] = ``
THREE.ShaderChunk['encodings_fragment'] = ``

const frameQueue = [];
function onFrame(cb) {
  frameQueue.push(cb);
  const index = frameQueue.length - 1;
  return function remove () {
    frameQueue.splice(index, 1);
  }
}

let getContext = () =>{}


const resizeCallbackQueue = []
function onResize(cb) {
  resizeCallbackQueue.push(cb);
  const index = resizeCallbackQueue.length - 1;
  return function remove () {
    resizeCallbackQueue.splice(index, 1);
  }
}
function observerContainer(container) {
  const observer = new ResizeObserver(entries=> {
    // eslint-disable-next-line no-unused-vars
    for (const entry of entries) {
      resizeCallbackQueue.forEach((cb) => {
        cb()
      })
    }
  })

  observer.observe(container);
}

function getElementSize(element) {
  const { left, top, width, height, bottom, right, x, y } =
        element.getBoundingClientRect()
  const size = {
    left,
    top,
    width,
    height,
    bottom,
    right,
    x,
    y,
  }
  return size
}

function rotateCamera(camera, azimuthAngle, polarAngle, yAxisUpSpaceInverse) {
  const copy = camera.position.clone();
  const spherical = new THREE.Spherical().setFromVector3( copy );
  spherical.theta = azimuthAngle
  spherical.phi = polarAngle
  spherical.makeSafe()
  camera.position.setFromSpherical( spherical )
    .applyQuaternion(yAxisUpSpaceInverse)
}

function creatEshadergradient({container, ...opt}) {
  if(!container) {
    return;
  }

  const renderer = new THREE.WebGLRenderer({
    antialias: true,
    alpha: true,
  });

  const linear = true;
  const LinearEncoding = 3000
  const sRGBEncoding = 3001
  renderer.outputEncoding = linear ? LinearEncoding : sRGBEncoding;
  const flat  = true;
  renderer.toneMapping = flat ? THREE.NoToneMapping : THREE.ACESFilmicToneMapping;

  const scene = new THREE.Scene();
  const size = getElementSize(container);
  const camera = new THREE.PerspectiveCamera( 45, size.width / size.height, 0.1, 1000 );

  renderer.setSize( size.width, size.height );
  // document.body.appendChild( renderer.domElement );

  // const geometry = new THREE.BoxGeometry( 1, 1, 1 );
  // const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
  // const cube = new THREE.Mesh( geometry, material );
  // scene.add( cube );

  const _AXIS_Y = Object.freeze( new THREE.Vector3( 0, 1, 0 ) )
  const _yAxisUpSpace = new THREE.Quaternion().setFromUnitVectors( camera.up, _AXIS_Y );
  const _yAxisUpSpaceInverse = _yAxisUpSpace.clone().invert();

  rotateCamera(camera, opt.cAzimuthAngle, opt.cPolarAngle, _yAxisUpSpaceInverse);
  window.camera = camera;

  camera.position.z = opt.cDistance;
  // camera.position.z = 10;

  container.append(renderer.domElement);


  const clock = new THREE.Clock();
  const ctx = {clock, scene, renderer, camera, size};

  getContext = ()=> ctx

  observerContainer(container);
  onResize(() => {
    const size = getElementSize(container);
    // ctx.size = size;
    Object.assign(ctx.size, size);
    camera.aspect = size.width / size.height
    camera.updateProjectionMatrix()
    // https://github.com/pmndrs/react-three-fiber/issues/178
    // Update matrix world since the renderer is a frame late
    camera.updateMatrixWorld()
    renderer.setSize( size.width, size.height );
  })

  const list = shaderGradient(opt)
  scene.add( ...list);



  function animate() {
    requestAnimationFrame( animate );
    // cube.rotation.x += 0.01;
    // cube.rotation.y += 0.01;
    const delta = clock.getDelta();

    frameQueue.forEach(frame => {
      frame(ctx, delta)
    })

    // renderer.render( scene, camera );
  }
  animate();
}

export {
  creatEshadergradient,
  onFrame,
  getContext,
  onResize
}
